#include "stdafx.h"

#include "map.h"
#include "entity/decoration.h"

#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "iostream"

namespace Demo {

Map::Map()
    :_width(0)
    ,_height(0)
    ,_hasChanged(true)
    ,_displayList(0)
{
}

Map::~Map()
{
}

void Map::create(int width, int height)
{
    destroy();
    {
        _texgroup.reset(new GLC::TextureGroup);
        _texgroup->load("dirt_1.png");
        _texgroup->load("dirt_2.png");
        _texgroup->load("dirt_3.png");
        _texgroup->load("dirt_4.png");
        _texgroup->load("dirt_5.png");
        _texgroup->load("dirt_6.png");
        _texgroup->load("dirt_7.png");
    }
    _width = 16;
    _height = 16;
    _hasChanged = true;
    {
        std::vector<Tile> tiles(_width * _height);
        _tiles.swap(tiles);

        //! TODO: remove random-based initialization with any meaningful mechanism
        srand(static_cast<unsigned>(time(NULL)));
        for (int x = 0; x < _width; ++x)
            for (int y = 0; y < _height; ++y)
            {
                int rnd = (rand() * 317 - 1281) % 11;
                if (rnd > 0 && rnd < 7)
                {
                    at(x, y).setGround(_texgroup->at(rnd));
                    at(x,y).setHeight(rnd%2);
                    _height_map[x][y] = ((float)(rnd%2))/4;
                }
                else
                {
                    at(x, y).setGround(_texgroup->at(0));
                    at(x,y).setHeight(0);
                     _height_map[x][y] = (float)0;
                }
            }
        for (int x = 0; x < _width; ++x)
            for (int y = 0; y < _height; ++y)
            {
                if ((x > 0))
                {
                    if (at(x-1,y).getHeight() < at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_BOTTOM, RAMPANT_TYPE_DOWN);
                    else if (at(x-1,y).getHeight() > at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_BOTTOM, RAMPANT_TYPE_UP);
                }
                if ((x < _width-1))
                {
                    if (at(x+1,y).getHeight() < at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_TOP, RAMPANT_TYPE_DOWN);
                    else if (at(x+1,y).getHeight() > at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_TOP, RAMPANT_TYPE_UP);
                }
                if ((y > 0))
                {
                    if (at(x,y-1).getHeight() < at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_RIGHT, RAMPANT_TYPE_DOWN);
                    else if (at(x,y-1).getHeight() > at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_RIGHT, RAMPANT_TYPE_UP);
                }
                if ((y < _height-1))
                {
                    if (at(x,y+1).getHeight() < at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_LEFT, RAMPANT_TYPE_DOWN);
                    else if (at(x,y+1).getHeight() > at(x,y).getHeight())
                        at(x,y).setPandus(TILE_SIDE_LEFT, RAMPANT_TYPE_UP);
                }
            }
    }
    //! TODO: remove demonstration-only code as soon as possible
    at(10, 10).entities().addEntity(IEntityPtr(new Decoration()));

    for (int x = 0; x < _width; ++x)
    {
        for (int y = 0; y < _height; ++y)
        {
            std::cout << _height_map[x][y];
        }
        std::cout << std::endl;
    }
    std::cout << mix(0,1,0,2,0.5);
}

void Map::destroy()
{
    _tiles.clear();
    _texgroup.reset(0);
}

GLC::Texture2DRef Map::getTileTexture(int x, int y) const
{
    return at(x, y).ground();
}

void Map::setTileTexture(int x, int y, GLC::Texture2DRef texture)
{
    return at(x, y).setGround(texture);
}

int Map::width() const
{
    return _width;
}

int Map::height() const
{
    return _height;
}

double Map::mix(double x0,double x1,double y0,double y1,double measure)
{
    double res = y0+(y1-y0)/(x1-x0)*(measure-x0);
    return res;
}

double  Map::f(double x,double y)const
{
    int i,j;
    double f00,f01,f10,f11;
    i = floor(x);
    j = floor(y);

    double res = _height_map[i][j];

    /*f00 = _height_map[i][j];
    f01 = _height_map[i][j+1];
    f10 = _height_map[i+1][j];
    f11 = _height_map[i+1][j+1];
    double res=(f00*(1-x)*(1-y)+f10*x*(1-y)+f01*(1-x)*y+f11*x*y);*/
    return res;
}

void Map::renderSmoothTile(int x,int y,int h00,int h01,int h10,int h11)const
{

}

void Map::renderSurface()const
{
    glColor3f(1,0,0);
    glBegin(GL_QUADS);
    for (int i=0;i!= 15;i++)
    {
        for (int j=0;j!= 15;j++)
        {
            glVertex3d(i,j,_height_map[i][j]);
            glVertex3d(i+1,j,_height_map[i+1][j]);
            glVertex3d(i+1,j+1,_height_map[i+1][j+1]);
            glVertex3d(i,j+1,_height_map[i][j+1]);
        }
    }
    glEnd();
    glColor3f(0,0,0);

    for (int i=0;i!= 15;i++)
    {
        for (int j=0;j!= 15;j++)
        {
            glBegin(GL_LINE_STRIP);
            glVertex3d(i,j,_height_map[i][j]+0.01);
            glVertex3d(i+1,j,_height_map[i+1][j]+0.01);
            glVertex3d(i+1,j+1,_height_map[i+1][j+1]+0.01);
            glVertex3d(i,j+1,_height_map[i][j+1]+0.01);
            glEnd();
        }
    }


}


void Map::render() const
{
    if (_hasChanged)
    {
        _hasChanged = false;
        if (_displayList)
        {
            glDeleteLists(_displayList, 1);
        }
        _displayList = glGenLists(1);
        glNewList(_displayList, GL_COMPILE_AND_EXECUTE);
        {
            //renderTiles();
            renderSurface();
            /*glColor3f(1,0,0);
            glBegin(GL_LINE_LOOP);
                for (int i = 0; i!= 10; i++)
                {
                    for (int j = 0; j!= 10; j++)
                    {
                        double fv = f(i,j,0,0,0,10);
                        glVertex3d(i,j,fv);
                    }

                }
            glEnd();*/
        }
        glEndList();
    }
    else
    {
        glCallList(_displayList);
    }
}

Tile& Map::at(int x, int y)
{
    return _tiles[x + y * _width];
}

const Tile& Map::at(int x, int y) const
{
    return _tiles[x + y * _width];
}

void Map::renderTiles() const
{
    glPushMatrix();
    glTranslatef(-width() / 4.0, - height() / 4.0, 0);
    for (int x = 0; x < width(); ++x)
    {
        glPushMatrix();
        glTranslatef(x, 0, 0);
        for (int y = 0; y < height(); ++y)
        {
            //at(x, y).render();
            glTranslatef(0, 1, 0);
        }
        glPopMatrix();
    }
    /*for (int i = 0; i < width()-1; ++i)
    {
        glPushMatrix();
        glTranslatef(i, 0, 0);
        for (int j = 0; j < height()-1; ++j)
        {
           // renderSmoothTile(h00,h01,h10,h11);
            renderSmoothTile(i,j,_height_map[i][j],_height_map[i+1][j],_height_map[i][j+1],_height_map[i+1][j+1]);

            glTranslatef(0, 1, 0);
        }
        glPopMatrix();
    }*/
    renderSmoothTile(0,0,0,0,0,0);

    glPopMatrix();
}

} // namespace Demo
